namespace ClassyBlog.Web.Specs.Unit
{
    using System;
    using System.Collections.Generic;

    using Machine.Specifications;

    using Infrastructure;

    [Subject(typeof(ArchivedDates))]
    public class when_year_parameter_name_is_set
    {
        private static string previous;

        Establish context = () => previous = ArchivedDates.YearParameterName;

        Because of = () => ArchivedDates.YearParameterName = "foo";

        It should_return_the_same = () =>
            ArchivedDates.YearParameterName.ShouldEqual("foo");

        Cleanup on_exist = () => ArchivedDates.YearParameterName = previous;
    }

    [Subject(typeof(ArchivedDates))]
    public class when_year_parameter_name_is_not_set
    {
        static string value;

        Establish context = () => ArchivedDates.YearParameterName = null;

        Because of = () => value = ArchivedDates.YearParameterName;

        It should_return_the_default = () => value.ShouldEqual("year");
    }

    [Subject(typeof(ArchivedDates))]
    public class when_month_parameter_name_is_set
    {
        private static string previous;

        Establish context = () => previous = ArchivedDates.MonthParameterName;

        Because of = () => ArchivedDates.MonthParameterName = "bar";

        It should_return_the_same = () =>
            ArchivedDates.MonthParameterName.ShouldEqual("bar");

        Cleanup on_exist = () =>
            ArchivedDates.MonthParameterName = previous;
    }

    [Subject(typeof(ArchivedDates))]
    public class when_month_parameter_name_is_not_set
    {
        static string value;

        Establish context = () => ArchivedDates.MonthParameterName = null;

        Because of = () => value = ArchivedDates.MonthParameterName;

        It should_return_the_default = () => value.ShouldEqual("month");
    }

    [Subject(typeof(ArchivedDates))]
    public class when_day_parameter_name_is_set
    {
        private static string previous;

        Establish context = () => previous = ArchivedDates.DayParameterName;

        Because of = () => ArchivedDates.DayParameterName = "baz";

        It should_return_the_same = () =>
            ArchivedDates.DayParameterName.ShouldEqual("baz");

        Cleanup on_exist = () => ArchivedDates.DayParameterName = previous;
    }

    [Subject(typeof(ArchivedDates))]
    public class when_day_parameter_name_is_not_set
    {
        static string value;

        Establish context = () => ArchivedDates.DayParameterName = null;

        Because of = () => value = ArchivedDates.DayParameterName;

        It should_return_the_default = () => value.ShouldEqual("day");
    }

    [Subject(typeof(ArchivedDates))]
    public class when_year_format_string_is_set
    {
        private static string previous;

        Establish context = () => previous = ArchivedDates.YearFormatString;

        Because of = () => ArchivedDates.YearFormatString = "foo";

        It should_return_the_same = () =>
            ArchivedDates.YearFormatString.ShouldEqual("foo");

        Cleanup on_exist = () => ArchivedDates.YearFormatString = previous;
    }

    [Subject(typeof(ArchivedDates))]
    public class when_year_format_string_is_not_set
    {
        static string value;

        Establish context = () => ArchivedDates.YearFormatString = null;

        Because of = () => value = ArchivedDates.YearFormatString;

        It should_return_the_default = () => value.ShouldEqual("yyyy");
    }

    [Subject(typeof(ArchivedDates))]
    public class when_month_format_string_is_set
    {
        private static string previous;

        Establish context = () => previous = ArchivedDates.MonthFormatString;

        Because of = () => ArchivedDates.MonthFormatString = "bar";

        It should_return_the_same = () =>
            ArchivedDates.MonthFormatString.ShouldEqual("bar");

        Cleanup on_exist = () => ArchivedDates.MonthParameterName = previous;
    }

    [Subject(typeof(ArchivedDates))]
    public class when_month_format_string_is_not_set
    {
        static string value;

        Establish context = () => ArchivedDates.MonthFormatString = null;

        Because of = () => value = ArchivedDates.MonthFormatString;

        It should_return_the_default = () => value.ShouldEqual("MMMM, yyyy");
    }

    [Subject(typeof(ArchivedDates))]
    public class when_day_format_string_is_set
    {
        private static string previous;

        Establish context = () => previous = ArchivedDates.DayFormatString;

        Because of = () => ArchivedDates.DayFormatString = "baz";

        It should_return_the_same = () =>
            ArchivedDates.DayFormatString.ShouldEqual("baz");

        Cleanup on_exist = () => ArchivedDates.DayParameterName = previous;
    }

    [Subject(typeof(ArchivedDates))]
    public class when_day_format_string_is_not_set
    {
        static string value;

        Establish context = () => ArchivedDates.DayFormatString = null;

        Because of = () => value = ArchivedDates.DayFormatString;

        It should_return_the_default = () => value.ShouldEqual("MMMM d, yyyy");
    }

    [Subject(typeof(ArchivedDates))]
    public class when_created_with_null_year_month_day
    {
        static ArchivedDates archivedDates;
        private static DateTime startAt;
        static DateTime endAt;

        Because of = () =>
        {
            archivedDates = new ArchivedDates(null, null, null);
            startAt = archivedDates.StartAt;
            endAt = archivedDates.EndAt;
        };

        It should_be_empty = () => archivedDates.IsEmpty.ShouldBeTrue();

        It should_return_same_start_at_as_clocks_minimum_date = () =>
            startAt.ShouldEqual(Clock.MinimumDate);

        It should_return_same_end_at_as_clocks_maximum_date = () =>
            endAt.ShouldEqual(Clock.MaximumDate);
    }

    [Subject(typeof(ArchivedDates))]
    public class when_created_with_year_month_and_day
    {
        const int Year = 2011;
        const int Month = 4;
        const int Day = 29;

        static ArchivedDates archivedDates;
        static DateTime startAt;
        static DateTime endAt;

        Establish context = () =>
            archivedDates = new ArchivedDates(Year, Month, Day);

        Because of = () =>
        {
            startAt = archivedDates.StartAt;
            endAt = archivedDates.EndAt;
        };

        It should_have_start_at_as_the_start_of_the_day = () =>
        {
            startAt.Year.ShouldEqual(Year);
            startAt.Month.ShouldEqual(Month);
            startAt.Day.ShouldEqual(Day);
            startAt.Hour.ShouldEqual(0);
            startAt.Minute.ShouldEqual(0);
            startAt.Second.ShouldEqual(0);
        };

        It should_have_end_at_as_the_end_of_the_day = () =>
        {
            endAt.Year.ShouldEqual(Year);
            endAt.Month.ShouldEqual(Month);
            endAt.Day.ShouldEqual(Day);
            endAt.Hour.ShouldEqual(23);
            endAt.Minute.ShouldEqual(59);
            endAt.Second.ShouldEqual(59);
        };

        It should_return_correct_to_string = () =>
            new DateTime(Year, Month, Day)
            .ToString(ArchivedDates.DayFormatString)
            .ShouldEqual(archivedDates.ToString());
    }

    [Subject(typeof(ArchivedDates))]
    public class when_created_with_year_and_month
    {
        const int Year = 2011;
        const int Month = 4;

        static ArchivedDates archivedDates;
        static DateTime startAt;
        static DateTime endAt;

        Establish context = () =>
            archivedDates = new ArchivedDates(Year, Month, null);

        Because of = () =>
        {
            startAt = archivedDates.StartAt;
            endAt = archivedDates.EndAt;
        };

        It should_have_start_at_as_the_start_of_the_month = () =>
        {
            startAt.Year.ShouldEqual(Year);
            startAt.Month.ShouldEqual(Month);
            startAt.Day.ShouldEqual(1);
            startAt.Hour.ShouldEqual(0);
            startAt.Minute.ShouldEqual(0);
            startAt.Second.ShouldEqual(0);
        };

        It should_have_end_at_as_the_end_of_the_month = () =>
        {
            endAt.Year.ShouldEqual(Year);
            endAt.Month.ShouldEqual(Month);
            endAt.Day.ShouldEqual(30);
            endAt.Hour.ShouldEqual(23);
            endAt.Minute.ShouldEqual(59);
            endAt.Second.ShouldEqual(59);
        };

        It should_return_correct_to_string = () =>
            new DateTime(Year, Month, 1)
            .ToString(ArchivedDates.MonthFormatString)
            .ShouldEqual(archivedDates.ToString());
    }

    [Subject(typeof(ArchivedDates))]
    public class when_created_with_year
    {
        const int Year = 2011;

        static ArchivedDates archivedDates;
        static DateTime startAt;
        static DateTime endAt;

        Establish context = () =>
            archivedDates = new ArchivedDates(Year, null, null);

        Because of = () =>
        {
            startAt = archivedDates.StartAt;
            endAt = archivedDates.EndAt;
        };

        It should_have_start_at_as_the_start_of_the_year = () =>
        {
            startAt.Year.ShouldEqual(Year);
            startAt.Month.ShouldEqual(1);
            startAt.Day.ShouldEqual(1);
            startAt.Hour.ShouldEqual(0);
            startAt.Minute.ShouldEqual(0);
            startAt.Second.ShouldEqual(0);
        };

        It should_have_end_at_as_the_end_of_the_year = () =>
        {
            endAt.Year.ShouldEqual(Year);
            endAt.Month.ShouldEqual(12);
            endAt.Day.ShouldEqual(31);
            endAt.Hour.ShouldEqual(23);
            endAt.Minute.ShouldEqual(59);
            endAt.Second.ShouldEqual(59);
        };

        It should_return_correct_to_string = () =>
            new DateTime(Year, 12, 1)
            .ToString(ArchivedDates.YearFormatString)
            .ShouldEqual(archivedDates.ToString());
    }

    [Subject(typeof(ArchivedDates))]
    public class when_creating_with_invalid_year
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new ArchivedDates(1990, 1, 1));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentOutOfRangeException>();
    }

    [Subject(typeof(ArchivedDates))]
    public class when_creating_with_invalid_month
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new ArchivedDates(2001, 0, 1));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentOutOfRangeException>();
    }

    [Subject(typeof(ArchivedDates))]
    public class when_creating_with_invalid_day
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new ArchivedDates(2001, 1, 32));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentOutOfRangeException>();
    }

    [Subject(typeof(ArchivedDates))]
    public class when_creating_with_year_and_day
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new ArchivedDates(2001, null, 1));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(ArchivedDates))]
    public class when_creating_with_month_and_day
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new ArchivedDates(null, 1, 1));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(ArchivedDates))]
    public class when_creating_with_month
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new ArchivedDates(null, 1, null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(ArchivedDates))]
    public class when_creating_with_day
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new ArchivedDates(null, null, 1));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(ArchivedDates))]
    public class when_calling_create_form_with_dictionary
    {
        static IDictionary<string, object> attributes;
        static ArchivedDates archivedDates;

        Establish context = () =>
            attributes = new Dictionary<string, object>
                         {
                            { ArchivedDates.YearParameterName, 2011 },
                            { ArchivedDates.MonthParameterName, 4 },
                            { ArchivedDates.DayParameterName, 30 }
                        };

        Because of = () =>
            archivedDates = ArchivedDates.CreateFrom(attributes);

        It should_have_same_year_which_exists_in_dictionary = () =>
            archivedDates.Year.ShouldEqual(2011);

        It should_have_same_month_which_exists_in_dictionary = () =>
            archivedDates.Month.ShouldEqual(4);

        It should_have_same_day_which_exists_in_dictionary = () =>
            archivedDates.Day.ShouldEqual(30);
    }
}